\section{Travail réalisé}

\subsection{La structure des threads}


\begin{lstlisting}
 struct thread_t {
        int			id;
        enum status		st;
        ucontext_t *		context;
        void *			retval;
};
\end{lstlisting}
Un thread est représenté par la structure suivante:

\begin{itemize}
\item id : qui permet d'identifier le thread de manière unique.
\item st : qui spécifie le statut du thread, il est soit READY , RUNNING , WAIT , KILLED.
\item context : qui représente le contexte du thread, on utilise la structure définie dans \textbf{<ucontext.h>}.
\item retval : qui correspond à la valeur de retour du thread accessible une fois que celui-ci a terminé.
\item pere : qui est un pointeur vers le thread appelant, dans le cas d'un thread\_join.
\end{itemize}


La partie la plus importante de cette structure est le champ \textit{context}, qui représente à lui seul l'idée de ce projet. Le passage d'un thread à un autre va en fait être une affaire de contexte, que l'on va sauvegarder dans cette structure avant de pourvoir passer un autre thread, et donc à son propre contexte. 

Nous avons pour ce faire utilisé les fonctions makecontext, getcontext et swapcontext, contenues dans la bibliothèque citée plus haut. 

\subsection{Manipulation des threads et implémentation des fonctions}

Pour manipuler les threads, nous utilisons trois FIFO. Chacune d'entre elles ne va contenir les threads propres à un statut, c'est à dire par exemple que thread\_FIFO\_WAIT ne sera utilisée que par les threads dont le statut est WAIT. 

\begin{lstlisting}
void init_main_thread(void)
\end{lstlisting}
~\\
Un des premiers problèmes qui se présente est celui du programme principal. Il faut que l'on puisse inclure celui-ci en tant que thread, sans pour autant que l'utilisateur n'en ait conscience.

Pour ce faire, une variable globale \textbf{init} est déclarée et initialisée à 0 dès la première utilisation de la bibliothèque. Dans chaque fonction de l'interface de gestion de thread, celle-ci est testée : si elle est nulle, on appelle la fonction init\_main\_thread() qui se charge d'initialiser une variable globale contenant le thread principal, de le spécifier comme étant le thread en cours, et passe la variable \textbf{init} à 1.

L'indice 0 est réservé au thread principal.


\begin{lstlisting}
int thread_create(thread_t *t,void *(*f)(void *),void *farg);
\end{lstlisting}
~\\

Lorsque l'on crée un thread, on l'ajoute à la FIFO thread\_FIFO\_READY. C'est lorsque l'on va faire un thread\_join() sur ce dernier que l'on va basculer vers son exécution.

Cependant, il faut d'abord initialiser l'ensemble de ses champs. 
\begin{itemize}
\item id reçoit la valeur d'une variable incrémentée à chaque nouveau thread.
\item st est initialisé à READY
\item retval, à null
\item context nécessite un traitement un peu plus particulier. Il nous faut d'abord récupérer le contexte courant gr\^ace à la fonction getcontext. La fonction makecontext permet ensuite de le compiler, en lui spécifiant la fonction à exécuter au lancement du thread.
\end{itemize}

Notre nouveau thread est ainsi prêt à être ajouté à la FIFO.\\


\begin{lstlisting}
extern int thread_yield(void);
\end{lstlisting}


L'appel de thread\_yield() passe la main au premier thread contenu par la FIFO thread\_FIFO\_READY. Pour ce faire, elle sauvegarde le contexte du thread courant dans une variable. Si ledit thread avait pour statut RUNNING, elle passe son statut à READY et l'ajoute dans la bonne file. Sinon, elle ne modifie pas son statut, et l'ajoute dans la file qui lui correspond. Enfin, elle prend le premier élément de thread\_FIFO\_READY et le déclare en tant que thread courant, après avoir modifié son statut en RUNNING.\\


\begin{lstlisting}
extern int thread_join(thread_t thread, void **retval);
\end{lstlisting}
thread\_join() permet préciser que le thread courant attend la fin de l'exécution du thread passé en paramètre pour continuer. Dans notre interface, c'est aussi grâce à cette fonction que les threads sont lancés.

Si le statut du thread passé en argument est KILLED, sa valeur de retour est copié dans la variable retval (à l'aide de la fonction memcpy). Ainsi, on peut passer à la seconde étape, qui est de libérer la mémoire du thread, et retourner.

Si le thread n'était pas fini, le thread courant est passé en statut WAIT, et ajouté à la FIFO correspondante. On remplit alors le champ pere du thread join, avec celui du thread jusqu'alors courant. Tant que le thread n'a pas pour statut KILLED, la fonction thread\_yield est appelée.
Lorsque le statut du thread passe à KILLED, et l'on agit comme dans le paragraphe précédent.

\begin{lstlisting}
extern thread_t thread_self(void);
\end{lstlisting}

Cette fonction se contente de nous donner le pointeur de la variable globale du thread courant (\textbf{current\_thread}).\\

\begin{lstlisting}
extern void thread_exit(void *retval);
\end{lstlisting}

Cette fonction remplit le champs retval de notre structure thread en la faisant pointer vers l'argument de la fonction thread\_exit(). Le statut du thread courant est alors passé en KILLED, et si son champ pere est rempli, on l'enlève de thread\_FIFO\_WAIT pour le repasser dans thread\_FIFO\_READY. Enfin, on fait un appel à  thread\_yield().\\
